package com.puzzlebazar.client.squaregrid.view;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import com.google.gwt.user.client.ui.AbsolutePanel;
import com.puzzlebazar.client.util.Recti;
import com.puzzlebazar.client.util.Vec2i;

public class DrawableLayer extends CompositeView {

	private final HashMap<Vec2i,Vector<Drawable> > drawables = new HashMap<Vec2i,Vector<Drawable> >();
	private final AbsolutePanel decorationPanel = new AbsolutePanel();

	/**
	 * Creates a layer of drawables
	 */
	public DrawableLayer() {
		super();
		initWidget( decorationPanel );
	}
	
	/**
	 * Adds a drawable to the layer
	 * 
	 * @param drawable Drawable to add
	 * @throws Exception
	 */
	public void addDrawable( Drawable drawable ) throws Exception {
		SGView view = getView();
		if( view == null ) return;

		Vec2i loc = drawable.getVertexFromLoc();
		
		if( !view.isValidVertex( loc ) ) throw new Exception( "Invalid cell" );
		Vector<Drawable> list = drawables.get(loc);
		if( list == null ) {
			list = new Vector<Drawable>();
			drawables.put( loc, list );
		}
		
		list.add( drawable );

		Vec2i pos = resizeDrawable( drawable ); 		
		decorationPanel.add( drawable.getWidget(), pos.x, pos.y );
	}

	/**
	 * Removes a single-vertex drawable from the decorator
	 * 
	 * @param loc Location of the single-vertex drawable
	 * @param name Drawable name (null to match any name)
	 * @throws Exception
	 */
	public void removeVertexDrawable( Vec2i loc, String name ) throws Exception {
		removeDrawableRect( loc, loc, name);
	}
	

	/**
	 * Removes a single-cell drawable from the decorator
	 * 
	 * @param loc Location of the single-cell drawable
	 * @param name Drawable name (null to match any name)
	 * @throws Exception
	 */
	public void removeCellDrawable( Vec2i loc, String name ) throws Exception {
		removeDrawableRect( loc, new Vec2i(loc.x+1, loc.y+1), name);
	}		
	
	/**
	 * Removes a multi-vertex drawable from the decorator
	 * 
	 * @param locFrom Location of one corner vertex of the drawable
	 * @param locTo Location of the diagonally opposite corner vertex of the drawable
	 * @param name Drawable name (null to match any name)
	 * @throws Exception
	 */
	public void removeVertexDrawable( Vec2i locFrom, Vec2i locTo, String name ) throws Exception {
		removeDrawableRect(
				Vec2i.getTopLeft(locFrom, locTo),
				Vec2i.getBottomRight(locFrom, locTo),
				name);	}

	/**
	 * Removes a multi-cell drawable from the decorator
	 * 
	 * @param locFrom Location of the top-left corner of the drawable
	 * @param locTo Location of the bottom-right corner of the drawable
	 * @param name Drawable name (null to match any name)
	 * @throws Exception
	 */
	public void removeCellDrawable( Vec2i locFrom, Vec2i locTo, String name ) throws Exception {
		removeDrawableRect(
				Vec2i.getTopLeftVertex(locFrom, locTo),
				Vec2i.getBottomRightVertex(locFrom, locTo),
				name);
	}	
	

	/**
	 * Removes a rectangle of drawables with well-ordered corners
	 * 
	 * @param locFrom Top-left vertex coordinate
	 * @param locTo Bottom-right vertex coordinate
	 * @param name Drawable name (null to match any name) 
	 * @throws Exception
	 */
	private void removeDrawableRect(Vec2i locFrom, Vec2i locTo, String name) throws Exception {

		Vector<Drawable> list = drawables.get( locFrom );
		if( list == null ) throw new Exception( "Removing unexisting drawable" );

		
		for (Iterator<Drawable> iterator = list.iterator(); iterator.hasNext();) {
			Drawable drawable = iterator.next();
			if( drawable.getVertexToLoc().equals( locTo ) ) {
				if( name == null || drawable.getName().equals( name ) ) {
					iterator.remove();
					decorationPanel.remove( drawable.getWidget() );
					return;
				}
			}
		}
		
		throw new Exception( "Removing unexisting drawable" );
	}
	

	/**
	 * Remove all drawables sharing the passed name
	 * 
	 * @param name Name of the drawable(s) to remove
	 */
	public void removeDrawable(String name) {
		
		assert( name != null );
		
		for (Iterator< Vector<Drawable> > iterator = drawables.values().iterator(); iterator.hasNext();) {
			Vector<Drawable> drawables = iterator.next();
			for (Iterator<Drawable> iterator2 = drawables.iterator(); iterator2.hasNext();) {
				Drawable drawable = iterator2.next();
				if( drawable.getName().equals( name ) ) {
					iterator2.remove();
					decorationPanel.remove( drawable.getWidget() );
				}			
			}
			if ( drawables.isEmpty() )
				iterator.remove();
		}

	}

	/**
	 * Make sure all images are correctly positioned and that they
	 * have to correct size
	 */
	private void repositionAllDrawables() {
		
		try {
			for (Iterator< Vector<Drawable> > drawablesIterator = drawables.values().iterator(); 
			drawablesIterator.hasNext();) {
				Vector<Drawable> drawableList = drawablesIterator.next();
				for (Iterator<Drawable> iterator = drawableList.iterator(); iterator.hasNext();) {
					Drawable drawable =  iterator.next();
					Vec2i pos;
					pos = resizeDrawable( drawable );
					decorationPanel.setWidgetPosition( drawable.getWidget(), pos.x, pos.y );
				}
			}
		} catch (Exception exception) {
			exception.printStackTrace();
		} 		
	}

	/**
	 * Resize a drawable and returns the precise position it should occupy
	 * 
	 * @param drawable The drawable to resize
	 * @return The position it should occupy, in pixels
	 * @throws Exception
	 */
	private Vec2i resizeDrawable(Drawable drawable) throws Exception {
		SGView view = getView();
		if( view == null ) return new Vec2i(0,0);
		Vec2i fromLoc = drawable.getVertexFromLoc();
		Vec2i toLoc = drawable.getVertexToLoc();
		Recti rect = new Recti( 
				view.getVertexPosition(fromLoc), 
				view.getVertexPosition(toLoc) );
		drawable.updateSize( rect.w, rect.h );
		Vec2i pos = new Vec2i( drawable.getRelativePos() );
		pos.x += rect.x;
		pos.y += rect.y;
		return pos;
	}

	@Override
	protected void notifyCellChanged() {
		repositionAllDrawables();
	}

	/**
	 * Remove all the widgets contained in this drawable
	 */
	public void clear() {
		drawables.clear();
		decorationPanel.clear();		
	}

}
